package com.algo.utils;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

public class IpLibrary {

	private String ipFile;
	private String storageSystem = "local";
	private Vector<Long> ipRange = new Vector<Long>();
	private Map<Long, String> ipLib = new HashMap<Long, String>();

	public IpLibrary(String inIpFile, String storageSystemType){
		ipFile = inIpFile;
		if (storageSystemType.toLowerCase().equals("hdfs")) {
			storageSystem = "hdfs";
		}
		try {
			init();
		} catch (NumberFormatException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

	}

	/*
	 * public void setStorageSystem(String storageSystemType) { if
	 * (storageSystemType.toLowerCase().equals("hdfs")){ storageSystem = "hdfs";
	 * } }
	 */

	private void init() throws NumberFormatException, IOException {
		if (storageSystem.equals("local")) {
			initIpLibLocal();
		} else {
			initIpLibHDFS();
		}
	}

	private void initIpLibLocal() throws NumberFormatException, IOException {
		BufferedReader br;
		br = new BufferedReader(new FileReader(ipFile));
		String str;

		while ((str = br.readLine()) != null) {
			String[] items = str.split("\\,");
			if (items.length >= 3) {
				long ipLong;
				if(items[0].contains("."))
					ipLong = ipToLong(items[0]);
				else
					ipLong = Long.parseLong(items[0]);
				ipRange.add(ipLong);
				ipLib.put(ipLong, items[2]);
			}
		}
		br.close();
	}

	private void initIpLibHDFS() throws NumberFormatException, IOException {
		FileSystem fs = FileSystem.get(URI.create(ipFile), new Configuration());
		FSDataInputStream fin = fs.open(new Path(ipFile));
		BufferedReader br = new BufferedReader(new InputStreamReader(fin));
		String str;

		while ((str = br.readLine()) != null) {
			String[] items = str.split("\\,");
			if (items.length >= 3) {
				long ipLong;
				if(items[0].contains("."))
					ipLong = ipToLong(items[0]);
				else
					ipLong = Long.parseLong(items[0]);
				ipRange.add(ipLong);
				ipLib.put(ipLong, items[2]);
			}
		}
		br.close();
	}

	private long ipToLong(String str) {
		if (str == null || !isValidIPAddress(str)) {
			return -1;
		}

		String[] ipArr = str.split("\\.");
		long ipl = 0;
		for (String s : ipArr) {
			ipl = ipl * 256 + Integer.parseInt(s);
		}
		return ipl;
	}

	private boolean isValidIPAddress(String str) {
		String temp = "";
		int tag = 0;
		if (str.charAt(0) == '.' || str.charAt(str.length() - 1) == '.')
			return false;
		for (int i = 0; i < str.length(); i++) {

			if (str.charAt(i) == '.') {
				tag++;
				if (Integer.parseInt(temp) > 255)
					return false;
				temp = "";
				continue;
			}
			if (str.charAt(i) < '0' || str.charAt(i) > '9')
				return false;
			temp += String.valueOf(str.charAt(i));
		}
		if (tag != 3)
			return false;
		return true;
	}

	private long Rangelower(long key) {
		int start = 0;
		int end = ipRange.size() - 1;
		int mid;
		while (start <= end) {
			mid = (end - start) / 2 + start;
			if (key < ipRange.get(mid)) {
				end = mid - 1;
			} else if (key > ipRange.get(mid)) {
				start = mid + 1;
			} else {
				return ipRange.get(mid);
			}
		}
		return ipRange.get(end);
	}

	public String getIPLocation(String ip) {
		long ipl = ipToLong(ip);
		if (ipl == -1) {
			return null;
		}
		return ipLib.get(Rangelower(ipToLong(ip)));
	}

	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		IpLibrary ipl = new IpLibrary(
				"E:\\IPlib-Region-000000000000000000000008-top100-20110728171818",
				"local");
		// ipl.init();
		System.out.println(ipl.getIPLocation("1.167.1.1"));
	}
}